const fs = require('fs')
const os = require('os')
const path = require('path')
const moment = require('moment')
const cron = require('node-cron')
const { Worker } = require('worker_threads')

// 定义定时任务，每分钟运行一次
cron.schedule('* * * * *', () => {
  // console.log('This task will run every minute')
  // 获取当前时间并格式化为标准时间格式
  const currentTime = moment().format('YYYY-w')
  const times = currentTime.split('-')

  // 设置为当前年的第6周
  const sixthWeekStart = moment().year(times[0]).isoWeek(times[1]).startOf('week')
  const sixthWeekEnd = moment().year(times[0]).isoWeek(times[1]).endOf('week')

  // 格式化日期
  const formattedStart = sixthWeekStart.format('YYYY-MM-DD HH:mm:ss')
  const formattedEnd = sixthWeekEnd.format('YYYY-MM-DD HH:mm:ss')

  // console.log(`第6周的起始时间: ${formattedStart}`)
  // console.log(`第6周的结束时间: ${formattedEnd}`)

  // // 打印日志信息，包括时间戳和具体内容
  // console.log(`[${currentTime}] Cron job executed: This task will run every day at ${currentTime}`)
})

// 私有化包列表
const storagePlugin_list = [
  'i18n-autoscan-cli',
  'verdaccio-custom',
  'CetVirtualTree',
  '@omega/admin',
  '@omega/app',
  '@omega/auth',
  '@omega/cli',
  '@omega/cli-codestd',
  '@omega/cli-devops',
  '@omega/cli-devserver',
  '@omega/dashboard',
  '@omega/fusion',
  '@omega/http',
  '@omega/i18n',
  '@omega/icon',
  '@omega/map',
  '@omega/schedule',
  '@omega/theme',
  '@omega/trend',
  '@omega/usercenter',
  '@omega/video',
  '@omega/virtualtree',
  '@omega/weather',
  '@omega/widget',
  'cet-chart',
  'cet-common',
  'cet-echarts-extend-map',
  'cet-eem-common',
  'cet-eem-common-test',
  'cet-eem-oilgasfield-common',
  'cet-graph',
  'cet-realtime',
  'cet-smart-water-material',
  'cet-task',
  'cet-video',
  'cet-waveform',
  'cettreeV2',
  '@omega/eslint-plugin',
  'lowcode-common',
  'fusion-manage',
  '@omega/eslint-config',
  '@omega/tsconfig',
  '@omega/utils',
  '@omega/vite-plugin-devserver',
  '@omega/chart',
  '@omega/core',
  'fusion-api',
  'fusion-subapp-vue3',
  'fusion-subapp',
  '@omega/layout',
  'pq-commonweb-package',
  '@omega/unity3d',
  'vue-project-package-split',
  '@omega/tracking',
  '@altair/lord',
  '@altair/knight',
  'cet-pq-common',
  '@altair/blade',
  '@omega/scalescreen',
  '@powercloud/common',
  '@altair/pack'
]

const useUtils = (_dbDir = './db', _logDir = 'E:/CET2025/verdaccio/logrotaelogs', _logName = '3th-verdaccio-out.log') => {
  const _storagePath = 'C:/Users/Administrator/AppData/Roaming/verdaccio/storage'

  /**
   * ### 同步写入文件
   * @param {*} dir 目录
   * @param {*} fileName 文件名
   * @param {*} data  数据
   */
  const asyncWriteFile = (dir, fileName, data) => {
    try {
      fs.writeFileSync(path.join(__dirname, fileName), JSON.stringify(data, null, 2))
    } catch (error) {
      const dirPath = path.join(__dirname, dir)
      fs.mkdirSync(dirPath, { recursive: true })
      fs.writeFileSync(path.join(__dirname, fileName), JSON.stringify(data, null, 2))
    }
  }

  /**
   * 同步读取文件
   * @param {*} fileName
   * @returns
   */
  const asyncReadFile = (fileName) => {
    try {
      const data = fs.readFileSync(path.join(__dirname, fileName), 'utf-8')
      return JSON.parse(data)
    } catch (error) {
      console.error(`读取 ${fileName} 失败:`, error.message)
      return null
    }
  }

  /**
   * 同步删除文件夹
   * @param {string} dirPath - 要删除的文件夹路径
   */
  const deleteFolderSync = (relativePath, isLoop) => {
    const dirPath = isLoop ? relativePath : path.join(__dirname, relativePath) // 构建绝对路径

    try {
      // 检查路径是否存在
      const stats = fs.statSync(dirPath)
      if (!stats.isDirectory()) {
        throw new Error(`路径 ${dirPath} 不是一个文件夹`)
      }

      // 读取文件夹内容
      const files = fs.readdirSync(dirPath)

      // 递归删除子文件和子文件夹
      for (const file of files) {
        const filePath = path.join(dirPath, file)

        const fileStats = fs.statSync(filePath)

        if (fileStats.isDirectory()) {
          // 如果是文件夹，递归删除
          deleteFolderSync(filePath, true)
        } else {
          // 如果是文件，直接删除
          fs.unlinkSync(filePath)
        }
      }

      // 删除空文件夹
      fs.rmdirSync(dirPath)
      console.log(` ${dirPath} ,删除文件夹成功`)
    } catch (err) {
      console.error(`${dirPath} 未找到,删除文件夹失败`)
      // throw err
    }
  }

  /**
   * ### 初始化插件删除缓存数据
   */
  const initPlugins = () => {
    // 默认删除db/tmp文件夹
    const dirList = ['./db', './tmp']
    dirList.forEach((dir) => {
      deleteFolderSync(dir)
    })
  }

  /**
   * ### 获取轮转日志文件列表
   * @param _logDir 日志目录
   * @param _logName 日志目录
   * @returns
   */
  const getLogDirFilesList = async () => {
    // 读取日志目录中的所有文件
    const logDirFiles = await fs.promises.readdir(_logDir)

    // 筛选出目标日志文件
    const file = logDirFiles
      .filter((file) => file.startsWith(_logName) && !file.endsWith('.gz')) // 筛选 非.gz 文件
      .map((file) => `${_logDir}/${file}`)
      .sort((a, b) => a.localeCompare(b)) // 按文件名排序

    return file
  }

  /**
   * ### 获取存储日志数据文件列表
   * @param dir 数据目录
   * @returns
   */
  const getDBDirFilesList = async (dir = 'db') => {
    let dbDirFiles = []

    // 读取日志目录中的所有文件
    try {
      dbDirFiles = await fs.promises.readdir(_dbDir)
    } catch (error) {
      const dirPath = path.join(__dirname, dir)
      fs.mkdirSync(dirPath, { recursive: true })
      dbDirFiles = await fs.promises.readdir(_dbDir)
    }

    // 筛选出目标日志文件
    const file = dbDirFiles.sort((a, b) => a.localeCompare(b)) // 按文件名排序

    return file
  }

  /**
   * ### 获取存储日志数据
   * @param dir 数据目录
   * @returns
   */
  const getDBStatsData = async (dir = './db') => {
    // 存储所有包的元数据
    const datebase = []
    // 读取日志目录中的所有文件
    const dbDirFiles = await fs.promises.readdir(_dbDir)

    dbDirFiles
      ?.sort((a, b) => a.localeCompare(b))
      ?.forEach((file) => {
        const filePath = path.join(dir, file)
        const dbPath = path.join(__dirname, filePath, 'datebase.json')

        if (fs.existsSync(dbPath)) {
          try {
            const content = fs.readFileSync(dbPath, 'utf-8')
            const db = JSON.parse(content)

            datebase.push({
              logNo: file,
              db
            })
          } catch (error) {
            console.error(`解析 ${file} 的 datebase.json 失败:`, error.message)
          }
        } else {
          console.warn(`db ${file} 没有 datebase.json 文件`)
        }
      })
    const filteredData = (inverse = true) => {
      return Object.fromEntries(Object.entries(mergeDatebase(datebase?.map((item) => item.db))).filter(([key, value]) => (inverse ? /\d+-\d+/.test(key) : !/\d+-\d+/.test(key))))
    }

    asyncWriteFile(`tmp`, `tmp/totalDatebase.json`, datebase)

    asyncWriteFile(`tmp`, `tmp/weeklyDatebase.json`, totalWeeklyData(filteredData()))

    return calculatePackageStats(filteredData(false))
  }

  /**
   * ### 处理按周统计数据
   * @param {*} input
   * @param {*} filter
   * @returns
   */
  const transformData = (input, filter = '') => {
    return Object.keys(input)
      .map((weekly) => {
        return Object.keys(input[weekly])
          .filter((name) => (filter ? name === filter : !filter))
          .map((name) => {
            const times = weekly.split('-')
            // 设置为当前年的第6周
            const sixthWeekStart = moment().year(times[0]).isoWeek(times[1]).startOf('week')
            const sixthWeekEnd = moment().year(times[0]).isoWeek(times[1]).endOf('week')
            return {
              weekly,
              timeRange: [sixthWeekStart.format('YYYY-MM-DD'), sixthWeekEnd.format('YYYY-MM-DD')],
              name,
              value: input[weekly][name]
            }
          })
      })
      .flat()
  }

  /**
   * ### 获取周统计数据
   * @returns
   */
  const getWeeklyDataByPackage = async () => {
    const weeklyData = asyncReadFile(`tmp/weeklyDatebase.json`)
    return transformData(weeklyData, 'cet-chart')
  }

  /**
   * ### 获取时间范围
   * @returns
   */
  const getTimeRange = () => {
    // 获取本周的时间范围
    const now = new Date()

    // 本日 Today
    const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate())
    const startTimestamp = startOfDay.getTime()

    const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)
    endOfDay.setMilliseconds(-1)
    const endTimestamp = endOfDay.getTime()

    // 最近七天
    const lastWeek = Date.now() - 7 * 24 * 60 * 60 * 1000

    // 最近一个月
    const lastMonth = new Date()
    lastMonth.setMonth(lastMonth.getMonth() - 1)

    // 获取本周的第一天（周一）
    const startOfWeek = new Date(now)
    startOfWeek.setDate(now.getDate() - now.getDay() + 1)
    startOfWeek.setHours(0, 0, 0, 0)

    // 获取本周的最后一天（周日）
    const endOfWeek = new Date(now)
    endOfWeek.setDate(now.getDate() - now.getDay() + 7)
    endOfWeek.setHours(23, 59, 59, 999)

    // 获取本月的时间范围
    // 获取本月的第一天
    const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
    startOfMonth.setHours(0, 0, 0, 0)

    // 获取本月的最后一天
    const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0)
    endOfMonth.setHours(23, 59, 59, 999)

    return {
      lastWeek: [lastWeek],
      lastMonth: [new Date(lastMonth).getTime()],
      dayStats: [startTimestamp, endTimestamp],
      thisWeek: [new Date(startOfWeek).getTime(), new Date(endOfWeek).getTime()],
      thisMonth: [new Date(startOfMonth).getTime(), new Date(endOfMonth).getTime()],
      //
      dayTime: [startTimestamp, endTimestamp],
      agoTime: [lastWeek, new Date(lastMonth).getTime()],
      weekTime: [new Date(startOfWeek).getTime(), new Date(endOfWeek).getTime()],
      monthTime: [new Date(startOfMonth).getTime(), new Date(endOfMonth).getTime()]
    }
  }

  /**
   * ### 读插件package数据
   */
  const readPackagesData = () => {
    // 存储所有包的元数据
    const packagesData = []

    // 遍历插件列表
    storagePlugin_list.forEach((plugin) => {
      const pluginPath = path.join(_storagePath, plugin)
      const packageJsonPath = path.join(pluginPath, 'package.json')

      // 检查是否存在 package.json 文件
      if (fs.existsSync(packageJsonPath)) {
        try {
          // 读取 package.json 文件内容
          const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8')
          const packageData = JSON.parse(packageJsonContent)
          const { latest } = packageData['dist-tags'] || {}
          const authorName = packageData?.versions[latest]?.author?.name || '--'

          // 将包信息添加到数组中
          packagesData.push({
            authorName,
            ...packageData
          })
        } catch (error) {
          // console.error(`解析 ${plugin} 的 package.json 失败:`, error.message)
        }
      } else {
        // console.warn(`插件 ${plugin} 没有 package.json 文件`)
      }
    })

    asyncWriteFile('tmp', `tmp/packages.json`, packagesData)
  }

  /**
   * ### 取插件package数据
   * @returns
   */
  const getPackagesData = () => {
    const fileName = `tmp/packages.json`
    return $utils.asyncReadFile(fileName)
  }

  /**
   * ### 过滤数据
   * @param {*} data
   * @param {*} result
   * @returns
   */
  const filterData = (data, result = {}) => {
    Object.entries(data).forEach(([packageName, count]) => {
      const name = decodeURIComponent(packageName)
      storagePlugin_list.forEach((pkg) => {
        if (name === pkg || (name.includes(pkg) && name.endsWith('.tgz'))) {
          result[name] = (result[name] || 0) + count
        }
      })
    })
    return result
  }

  /**
   * ### 过滤插件包更新日志数据
   * @param {*} data
   * @param {*} result
   * @returns
   */
  const filterPutLogsData = (data, result = {}) => {
    Object.entries(data).forEach(([packageName, count]) => {
      const name = decodeURIComponent(packageName)
      storagePlugin_list.forEach((pkg) => {
        if (name === pkg || (name.includes(pkg) && name.endsWith('.tgz'))) {
          result[name] = count
        }
      })
    })
    return result
  }

  const filterDatebase = (data, result = {}) => {
    Object.entries(data).forEach(([packageName, count]) => {
      const name = decodeURIComponent(packageName)
      result[name] = (result[name] || 0) + count
    })
    return result
  }

  /**
   * ### 过滤计算数据
   * @param {*} pkg
   * @param {*} data
   * @param {*} result
   * @param {*} regex
   * @returns
   */
  const filterCalculateData = (pkg, data, result = [], regex) => {
    let num = 0

    for (const key in data) {
      if (data.hasOwnProperty(key)) {
        if (key === pkg || regex.test(key)) {
          num += data[key]
          const index = result.findIndex((item) => item.packageName === key)
          if (index !== -1) return

          result.push({
            packageName: key,
            value: data[key] || 0
          })
        }
      }
    }
    return num
  }

  /**
   * ### 计算包下载量统计
   * @param {*} logs
   * @returns
   */
  const calculatePackageStats = (logs) => {
    const packageTotal = []
    const packageDetail = {}
    const lastWeekLogs = []
    const lastMonthLogs = []
    const thisWeekLogs = []
    const thisMonthLogs = []

    const packagesData = $utils.getPackagesData()

    storagePlugin_list.forEach((pkg) => {
      let totalDownloads = 0
      let totalWeekDownloads = 0
      let totalMonthDownloads = 0
      let totalthisWeekDownloads = 0
      let totalthisMonthDownloads = 0

      packageDetail[pkg] = {
        total: 0,
        details: []
      }
      const pattern = `${pkg}/-/.*.tgz`
      const regex = new RegExp(pattern)

      for (const key in logs.packages) {
        if (logs.packages.hasOwnProperty(key)) {
          if (key === pkg || regex.test(key)) {
            totalDownloads += logs.packages[key]

            packageDetail[pkg].details.push({
              packageName: key,
              version: key,
              value: logs.packages[key] || '--',
              dayDownloads: logs.dayStats[key] || '--',
              weekDownloads: logs.lastWeek[key] || '--',
              monthDownloads: logs.lastMonth[key] || '--',
              thisWeekDownloads: logs.thisWeek[key] || '--',
              thisMonthDownloads: logs.thisMonth[key] || '--'
            })
          }
        }
      }

      totalWeekDownloads = $utils.filterCalculateData(pkg, logs.lastWeek, lastWeekLogs, regex)
      totalMonthDownloads = $utils.filterCalculateData(pkg, logs.lastMonth, lastMonthLogs, regex)
      totalthisWeekDownloads = $utils.filterCalculateData(pkg, logs.thisWeek, thisWeekLogs, regex)
      totalthisMonthDownloads = $utils.filterCalculateData(pkg, logs.thisMonth, thisMonthLogs, regex)

      const packageData = packagesData.find((item) => item.name === pkg)
      packageDetail[pkg].total = totalDownloads
      packageTotal.push({
        packageName: pkg,
        version: packageData?.['dist-tags']?.['latest'] || '--',
        authorName: packageData?.['authorName'] || '--',
        value: totalDownloads,
        updateNameIp: logs.putLogs[pkg] || '--',
        totalWeekDownloads,
        totalMonthDownloads,
        totalthisWeekDownloads,
        totalthisMonthDownloads,
        details: packageDetail[pkg].details
      })
    })

    return {
      downloadLogs: packageTotal,
      config: this.config,
      tableList: {
        lastWeekLogs: lastWeekLogs.sort((a, b) => b.value - a.value),
        lastMonthLogs: lastMonthLogs.sort((a, b) => b.value - a.value),
        thisWeekLogs: thisWeekLogs.sort((a, b) => b.value - a.value),
        thisMonthLogs: thisMonthLogs.sort((a, b) => b.value - a.value)
      }
    }
  }

  // 合并 Worker 线程的结果
  const mergeResults = (results) => {
    const obj = {}
    let keyList = []

    results.forEach((i) => {
      Object.keys(i)?.forEach((j) => {
        keyList.push(j)
      })
    })

    keyList.forEach((key) => {
      obj[key] = {}
    })

    results.forEach((result) => {
      Object.keys(result)?.forEach((i) => {
        if (i === 'putLogs') {
          $utils.filterPutLogsData(result[i], obj[i])
        } else {
          $utils.filterData(result[i], obj[i])
        }
      })
    })

    return obj
  }

  // 合并 Worker 线程的结果
  const mergeDatebase = (results) => {
    const obj = {}
    let keyList = []

    results.forEach((i) => {
      Object.keys(i)?.forEach((j) => {
        keyList.push(j)
      })
    })

    keyList.forEach((key) => {
      obj[key] = {}
    })

    results.forEach((result) => {
      Object.keys(result)?.forEach((i) => {
        if (i === 'putLogs') {
          $utils.filterPutLogsData(result[i], obj[i])
        } else {
          $utils.filterDatebase(result[i], obj[i])
        }
      })
    })

    return obj
  }

  const getCurrentMonthFormatted = () => {
    const now = new Date()
    const year = now.getFullYear()
    const month = String(now.getMonth() + 1).padStart(2, '0') // 月份从0开始，需要加1，并补零
    return `${year}${month}`
  }

  // 周数据求和
  const totalWeeklyData = (weeklyData) => {
    const filterData = {}

    storagePlugin_list.forEach((pkg) => {
      const pattern = `${pkg}/-/.*.tgz`
      const regex = new RegExp(pattern)

      for (const key in weeklyData) {
        if (!filterData[key]) {
          filterData[key] = {}
        }

        let total = 0

        for (const prop in weeklyData[key]) {
          if (prop === pkg || regex.test(prop)) {
            total += weeklyData[key][prop]
          }
        }

        if (total > 0) {
          filterData[key][pkg] = total
        }
      }
    })

    return filterData
  }

  return {
    getLogDirFilesList,
    getDBDirFilesList,
    getDBStatsData,
    getTimeRange,
    readPackagesData,
    filterData,
    filterPutLogsData,
    filterDatebase,
    filterCalculateData,
    calculatePackageStats,
    mergeResults,
    asyncWriteFile,
    asyncReadFile,
    getPackagesData,
    deleteFolderSync,
    initPlugins,
    getCurrentMonthFormatted,
    getWeeklyDataByPackage
  }
}
const $utils = useUtils()

const useWorker = () => {
  let timer = null
  let number = 0

  /**
   * ### 将日志文件拆分为多个部分
   * @param {string} logFilePath - 日志文件路径
   * @param {number} numWorkers - Worker 数量
   * @returns {Array} - 分割后的日志文件部分
   */
  const splitLogFile = (logFilePath, numWorkers) => {
    const fileSize = fs.statSync(logFilePath).size
    const num = fileSize < 1000000000 ? 1 : numWorkers
    const chunkSize = Math.ceil(fileSize / num)
    const chunks = []

    for (let i = 0; i < numWorkers; i++) {
      const start = i * chunkSize
      const end = (i + 1) * chunkSize
      chunks.push({ start, end })
    }

    return chunks
  }

  /**
   * ### 启动 Worker 线程
   * @param {string} logFilePath - 日志文件路径
   * @param {Array} chunks - 分割后的日志文件部分
   * @param {*} logNo - 日志编号
   * @returns
   */
  const startWorkers = (logFilePath, chunks, logNo) => {
    const timeRange = $utils.getTimeRange()

    return new Promise((resolve, reject) => {
      const workers = []
      const results = []
      let completedWorkers = 0

      chunks.forEach((chunk, index) => {
        const workerPath = path.resolve(__dirname, 'worker.js')
        const worker = new Worker(workerPath, {
          workerData: {
            isPm2Hosting: true,
            logFilePath,
            start: chunk.start,
            end: chunk.end,
            timeRange
          }
        })

        worker.on('message', (result) => {
          results[index] = result
          completedWorkers++

          // 所有 Worker 线程完成后，合并结果
          if (completedWorkers === chunks.length) {
            // 合并线程结果
            const mergedResult = $utils.mergeResults(results)
            // 数据统计
            const packageStats = $utils.calculatePackageStats(mergedResult)

            resolve(packageStats)

            const dir = `db/${logNo}`
            $utils.asyncWriteFile(`db/${logNo}`, `${dir}/results.json`, results)
            $utils.asyncWriteFile(`db/${logNo}`, `${dir}/results.json`, results)
            $utils.asyncWriteFile(`db/${logNo}`, `${dir}/datebase.json`, mergedResult)
            $utils.asyncWriteFile(`db/${logNo}`, `${dir}/stats.json`, packageStats)

            // 单次循环结束清除定时器
            if (timer) {
              console.log('所有 Worker 线程已完成, 读取日志文件成功')
              clearInterval(timer)
              timer = null
              number = 0
            }
          }
        })

        worker.on('error', (error) => {
          console.error(`Worker ${index} 出错:`, error)
          reject(`Worker ${index} 出错:`, error)
        })

        worker.on('exit', (code) => {
          if (code !== 0) {
            console.error(`Worker ${index} 退出，代码: ${code}`)
            reject(`Worker ${index} 退出，代码: ${code}`)
          }
        })

        workers.push(worker)
      })
    })
  }

  /**
   * ### 多线程主函数
   * @param {string} file - 日志文件路径
   * @param {*} index - 日志编号
   */
  const main = async (file, index) => {
    // 获取 CPU 核心数
    const cpuCount = os.cpus().length
    console.log(`当前电脑 CPU ${cpuCount} 核, 即将启动多线程读取日志文件`)

    timer = setInterval(() => {
      number++
      console.log(`${number}`)
    }, 1000)

    const chunks = splitLogFile(file, cpuCount)
    await startWorkers(file, chunks, index)
  }

  return {
    main
  }
}
const $worker = useWorker()

/**
 * ### 动态读取轮转日志文件
 */
const readRotatedLogs = async () => {
  try {
    // 筛选出目标日志文件
    const logFiles = await $utils.getLogDirFilesList()

    const storageFile = await $utils.getDBDirFilesList()

    const currentMonth = $utils.getCurrentMonthFormatted()

    for (const file of logFiles) {
      // 日志编号
      const logNo = file.match(/\.(\d+)/)?.[1] || 'tmp'

      // 每次读取未轮转的实时日志
      if (!storageFile.includes(logNo) || logNo === 'tmp' || logNo == currentMonth) {
        await $worker.main(file, logNo)
      }
    }
  } catch (err) {
    console.error('读取日志失败：', err)
  }
}

/**
 * ### 初始化数据
 */
const initData = async () => {
  // $utils.initPlugins()

  // 解析package.json
  await $utils.readPackagesData()

  // 解析日志
  await readRotatedLogs()

  // 合并轮转日志数据
  const dbStatsData = await $utils.getDBStatsData()
  const dbWeekData = await $utils.getWeeklyDataByPackage('@omega/icon')

  // 写入本地缓存
  $utils.asyncWriteFile(`tmp`, `tmp/dbStatsData.json`, dbStatsData)
  $utils.asyncWriteFile(`tmp`, `tmp/dbWeekData.json`, dbWeekData)

  return dbStatsData
}
const init = async () => {
  const data = await initData()

  console.log()
}
init()
